﻿using AntlrTS.LangFundmental;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AntlrTS.LanguagePatterns
{
    public class GenAstVisitor: TSBaseVisitor<LangObject>
    {
        public TSAST ast = new TSAST();

        void addOps(List<OperatorExpr.Operators> ops, string opstr)
        {
            if (opstr == "+")
            {
                ops.Add(OperatorExpr.Operators.ADD);
            }
            else if (opstr == "-")
            {
                ops.Add(OperatorExpr.Operators.SUB);
            }
            else if (opstr == "*")
            {
                ops.Add(OperatorExpr.Operators.MUL);
            }
            else if (opstr == "/")
            {
                ops.Add(OperatorExpr.Operators.DIV);
            }
            else if (opstr == "&&")
            {
                ops.Add(OperatorExpr.Operators.ADD);
            }
            else if (opstr == "||")
            {
                ops.Add(OperatorExpr.Operators.OR);
            }
            else if (opstr == "!")
            {
                ops.Add(OperatorExpr.Operators.NOT);
            }
            else if (opstr == "<=")
            {
                ops.Add(OperatorExpr.Operators.LE);
            }
            else if (opstr == ">=")
            {
                ops.Add(OperatorExpr.Operators.GE);
            }
            else if (opstr == "==")
            {
                ops.Add(OperatorExpr.Operators.EQ);
            }
            else if (opstr == "!=")
            {
                ops.Add(OperatorExpr.Operators.NE);
            }
            else
            {
                Console.WriteLine("Unknown operator");
                /*language error*/
            }
        }

        public override LangObject VisitTsfile(TSParser.TsfileContext context)
        {
            foreach(var i in context.children)
            {
                Visit(i);
            }
            return new LangObject();
        }

        public override LangObject VisitModuleexpr(TSParser.ModuleexprContext context)
        {
            var childrens = context.children;
            foreach (var i in childrens)
            {
                if (i.GetText() != "module")
                {
                    if (ast.moduleName == "")
                    {
                        ast.moduleName = i.GetText();
                    }
                    else
                    {
                        /*multi module name error */
                    }
                }
            }
            return new LangObject();
        }

        public override LangObject VisitUsingexpr(TSParser.UsingexprContext context)
        {
            var x = new UsingExpr(); 
            if(context.ChildCount>2)
            {
                x.alias.Add(context.modulenamealias().GetText());
            }
            x.moduleName = VisitModulename(context.modulename()).toRawString();
            ast.usingExprs.Add(x);
            return new LangObject();
        }

        public override LangObject VisitModulenamealias(TSParser.ModulenamealiasContext context)
        {
            return new LangObject(context.GetText());
        }

        public override LangObject VisitModulename(TSParser.ModulenameContext context)
        {
            return new LangObject(context.GetText());
        }

        public override LangObject VisitDefs(TSParser.DefsContext context)
        {
            foreach(var i in context.children)
            {
                if(i is TSParser.ConstructorContext)
                {
                    ast.typeConstructors.Add(Visit(i) as TypeConstructors);
                }
                else if(i is TSParser.FunctiondefContext)
                {
                    ast.tsFunctions.Add(Visit(i) as TSFunction);
                }
                
            }
            return new LangObject();
        }

        public override LangObject VisitConstructor(TSParser.ConstructorContext context)
        {
            var curTypeConstructors = new TypeConstructors();
            foreach (var i in context.children)
            {
                if (i.ChildCount == 0)/*终结符*/
                {
                    var terminalName = i.GetText();
                    if (terminalName != "type" && terminalName != ":=")
                    {
                        curTypeConstructors.typeSchemes.Add(new TypeScheme(terminalName));
                        Console.WriteLine("typescheme:" + terminalName + "\n");
                    }
                }
                else if(i is TSParser.ConstructornameContext)
                {
                    curTypeConstructors.type = new TSTypeAST(i.GetText()).getThis();
                }
                else if(i is TSParser.ProductionConstructorContext)
                {
                    var tempPC = VisitProductionConstructor(i as TSParser.ProductionConstructorContext) as ProductionCones;
                    foreach (var item in tempPC.pcs)
                    {
                        item.fatherType = curTypeConstructors.type;
                        
                    }
                    curTypeConstructors.productionConstructor=tempPC.pcs;
                }
            }
            return curTypeConstructors;
        }

        public override LangObject VisitConstructorname(TSParser.ConstructornameContext context)
        {
            return new LangObject(context.GetText());
        }

        public override LangObject VisitProductionConstructor(TSParser.ProductionConstructorContext context)
        {
            ProductionCones p = new ProductionCones();
            var x = new ProductionConstructor();
            x.type = new TSTypeAST(context.constructorname().GetText()).getThis();
            foreach (var item in context.type())
            {
                x.typeSchemeOrTSType.Add(Visit(item));
            }
            p.pcs.Add(x);
            if (context.productionConstructor()!=null)
            {
                p.pcs.AddRange((Visit(context.productionConstructor()) as ProductionCones).pcs);
            }
            return p;
        }

        public override LangObject VisitFunctiondef(TSParser.FunctiondefContext context)
        {
            TSFunction x=new TSFunction();
            foreach(var i in context.children)
            {
                if(i is TSParser.FunctionContext)
                {
                    x = Visit(i) as TSFunction;
                }
                else if(i is TSParser.FunctionannotionContext)
                {
                    /*to do*/
                   //FunctionAnnoation f=Visit(i) as FunctionAnnoation;
                   //ast.globalAnnoation.Add(f.rawStringName, f);
                }
            }
            return x;
        }

        public override LangObject VisitFunction(TSParser.FunctionContext context)
        {
            TSFunction ts = new TSFunction();
            foreach (var item in context.children)
            {
                if(item is TSParser.FunctionnameContext)
                {
                    FunctionName temp = Visit(item) as FunctionName;
                    ts.functionName = temp.functionName;
                    ts.moduleName = temp.moduleName;
                }
                else if(item is TSParser.DotvariablesContext)
                {
                    ts.parameterVariable = GetDotVariables.getVariables(item);
                }
                else if(item is TSParser.FunctionbodyContext)
                {
                    ts.elv = Visit(item) as EveluableExpr;
                }
            }
            return ts;
        }

        public override LangObject VisitFunctionbody(TSParser.FunctionbodyContext context)
        {
            return Visit(context.eveluableexpr());
        }

        public override LangObject VisitPatternexpr(TSParser.PatternexprContext context)
        {
            return new LangObject();
        }

        public override LangObject VisitMatchexpr(TSParser.MatchexprContext context)
        {
            MatchExpr temp = new MatchExpr();
            foreach (var item in context.children)
            {
                if(item is TSParser.DotvariablesContext)
                {
                    temp.parameterVariable = GetDotVariables.getVariables(item) as List<Variable>;
                }
                else if(item is TSParser.PatternbodyContext)
                {
                    temp.patternBody = Visit(item) as PatternBody;
                }
                else if(item is TSParser.WhereblockContext)
                {
                    //to do
                    //temp.whereBlockVariable=Vi
                }
            }
            return temp;
        }

        public override LangObject VisitCaseexpr(TSParser.CaseexprContext context)
        {
            CaseExpr temp = new CaseExpr();
            foreach (var item in context.children)
            {
                if (item is TSParser.DotvariablesContext)
                {
                    temp.parameterVariable = GetDotVariables.getVariables(item) as List<Variable>;
                }
                else if (item is TSParser.PatternbodyContext)
                {
                    temp.patternBody = Visit(item) as PatternBody;
                }
            }
            return temp;
        }

        public override LangObject VisitPatternbody(TSParser.PatternbodyContext context)
        {
            PatternBody pb = new PatternBody();
            List<LangObject> patterns = new List<LangObject>();
            Pattern tempPattern = new Pattern();
            EveluableExpr tempExpr = new EveluableExpr() ;
            foreach (var item in context.children)
            {
                if(item is TSParser.PatternContext)
                {
                    patterns = (Visit(item) as ListPatterns).patterns;                
                }
                else if(item is TSParser.EveluableexprContext)
                {
                    tempExpr = Visit(item) as EveluableExpr;
                }
                else if(item is TSParser.PatternbodyContext)
                {
                    foreach (var i in ((Visit(item) as PatternBody).patterns))
                    {
                        pb.patterns.Add(i.Key,i.Value);
                    }
                }
            }
            pb.patterns.Add(patterns, tempExpr);
            return pb;
        }

        public override LangObject VisitPattern(TSParser.PatternContext context)
        {
            return Visit(context.constructorPattern());
        }

        public override LangObject VisitConstantsWithVariable(TSParser.ConstantsWithVariableContext context)
        {
            ContantWithVariable cs = new ContantWithVariable();
            foreach (var i in context.children)
            {
                if(i is TSParser.ConstantContext)
                {
                    cs.contantWithVariables.Add(Visit(i) as Constant);
                }
                else
                {
                    cs.contantWithVariables.Add(new Variable(i.GetText()));
                }
            }
            return new LangObject();
        }

        public override LangObject VisitConstructorPattern(TSParser.ConstructorPatternContext context)
        {
            ListPatterns patterns = new ListPatterns();
            foreach (var item in context.children)
            {
                patterns.patterns.Add(Visit(item));
            }
            return patterns;
        }

        public override LangObject VisitMultipattern(TSParser.MultipatternContext context)
        {
            ConstructorPattern cp = new ConstructorPattern();
            cp.constructorName = context.constructorname().GetText();
            cp.constantOrVariables = (Visit(context.constantsWithVariable()) as ContantWithVariable).contantWithVariables;
            return cp;
        }

        public override LangObject VisitIfelseexpr(TSParser.IfelseexprContext context)
        {
            IfElseExpr ife_expr = new IfElseExpr();
            List<EveluableExpr> exprs = new List<EveluableExpr>();
            foreach (var item in context.children)
            {
                if (item is TSParser.EveluableexprContext)
                {
                    exprs.Add(Visit(item) as EveluableExpr);
                }
            }
            Debug.Assert(exprs.Count == 3, "If else expr 总数不等于3");
            ife_expr.ifExpr = exprs[0];
            ife_expr.thenExpr = exprs[1];
            ife_expr.elseExpr = exprs[2];
            return ife_expr;
        }

        public override LangObject VisitLetexpr(TSParser.LetexprContext context)
        {
            LetExpr letExpr = new LetExpr();
            foreach (var i in context.children)
            {
                if(i is TSParser.AssignvariablesContext)
                {
                    letExpr.variables = (Visit(i) as ListAssignVariable).tsfs;
                }
                else if(i is TSParser.EveluableexprContext)
                {
                    letExpr.elv = Visit(i) as EveluableExpr;
                }
            }
            return letExpr;
        }

        public override LangObject VisitWhereblock(TSParser.WhereblockContext context)
        {
            ListAssignVariable temp = new ListAssignVariable();
            foreach (var i in context.children)
            {
                if (i is TSParser.AssignvariablesContext)
                {
                    temp.tsfs=(Visit(i) as ListAssignVariable).tsfs;
                }
            }
            return temp;
        }

        public override LangObject VisitParameterpack(TSParser.ParameterpackContext context)
        {
            if(context.ChildCount>2)
            {
                foreach (var item in context.children)
                {
                    if(item is TSParser.DotparametersContext)
                    {
                        return Visit(item);
                    }
                }
            }
            return new Parameters();
        }

        public override LangObject VisitDotparameters(TSParser.DotparametersContext context)
        {
            Parameters ps = new Parameters();
            foreach (var i in context.children)
            {
                if(i is TSParser.ParameterContext)
                {
                    ps.patameters.Add(Visit(i) as OperatorExpr);
                }
                else if(i is TSParser.DotparametersContext)
                {
                    ps.patameters.AddRange((Visit(i) as Parameters).patameters);
                }
            }
            return ps;
        }

        public override LangObject VisitFunctionApp(TSParser.FunctionAppContext context)
        {
            FunctionApp fapp = new FunctionApp();
            fapp.name = Visit(context.functionname()) as FunctionName;
            foreach (var i in context.parameterpack())
            {
                fapp.parameter.Add(Visit(i) as Parameters);
            }
            return fapp;
        }

        public override LangObject VisitAssignvariables(TSParser.AssignvariablesContext context)
        {
            ListAssignVariable temp = new ListAssignVariable();
            foreach (var i in context.children)
            {
                if(i is TSParser.FunctionnameOrVariableContext)
                {
                    TSFunction tempFunc = new TSFunction();
                    foreach (var j in (i as TSParser.FunctionnameOrVariableContext).children)
                    {
                        if (j is TSParser.FunctionnameContext)
                        {
                            tempFunc.functionName = (Visit(j) as FunctionName).functionName;
                            tempFunc.moduleName = (Visit(j) as FunctionName).moduleName;
                        }
                        else if (j is TSParser.OperatorexprContext)
                        {
                            tempFunc.elv = Visit(j) as OperatorExpr;
                        }
                    }
                    temp.tsfs.Add(tempFunc);
                }
                else if(i is TSParser.AssignvariablesContext)
                {
                    temp.tsfs.AddRange((Visit(i) as ListAssignVariable).tsfs);
                }
            }
            return temp;
        }


        //public LangObject VisitOperatorexpr(TSParser.OperatorexprContext context)
        //{
        //    throw new NotImplementedException();
        //}

        public override LangObject VisitOperatorexpr3(TSParser.Operatorexpr3Context context)
        {
            if (context.ChildCount > 1)
            {
                var children = context.children;
                List<OperatorExpr> exprs = new List<OperatorExpr>();
                List<OperatorExpr.Operators> ops = new List<OperatorExpr.Operators>();
                foreach (var i in children)
                {
                    if (i is TSParser.Operatorexpr2Context)
                    {
                        exprs.Add(Visit(i) as OperatorExpr);
                    }
                    else
                    {
                        var opstr = i.GetText();
                        addOps(ops, opstr);
                    }
                }
                OperatorExpr opexpr = new OperatorExpr();
                Debug.Assert(exprs.Count - 1 == ops.Count, "expr-1 should equal ops");
                opexpr.opexprs = exprs;
                opexpr.ops = ops;
                return opexpr;
            }
            else
            {
                return Visit(context.operatorexpr2()[0]);
            }
        }

        public override LangObject VisitOperatorexpr2(TSParser.Operatorexpr2Context context)
        {
            if (context.ChildCount > 1)
            {
                var children = context.children;
                List<OperatorExpr> exprs = new List<OperatorExpr>();
                List<OperatorExpr.Operators> ops = new List<OperatorExpr.Operators>();
                foreach (var i in children)
                {
                    if (i is TSParser.Operatorexpr1Context)
                    {
                        exprs.Add(Visit(i) as OperatorExpr);
                    }
                    else
                    {
                        var opstr = i.GetText();
                        addOps(ops, opstr);
                    }
                }
                OperatorExpr opexpr = new OperatorExpr();
                Debug.Assert(exprs.Count - 1 == ops.Count, "expr-1 should equal ops");
                opexpr.opexprs = exprs;
                opexpr.ops = ops;
                return opexpr;
            }
            else
            {
                return Visit(context.operatorexpr1()[0]);
            }
        }

        public override LangObject VisitOperatorexpr1(TSParser.Operatorexpr1Context context)
        {
            if (context.ChildCount > 1)
            {
                var children = context.children;
                List<OperatorExpr> exprs = new List<OperatorExpr>();
                List<OperatorExpr.Operators> ops = new List<OperatorExpr.Operators>();
                foreach (var i in children)
                {
                    if (i is TSParser.Operatorexpr0Context)
                    {
                        exprs.Add(Visit(i) as OperatorExpr);
                    }
                    else
                    {
                        var opstr = i.GetText();
                        addOps(ops, opstr);
                    }
                }
                OperatorExpr opexpr = new OperatorExpr();
                Debug.Assert(exprs.Count - 1 == ops.Count, "expr-1 should equal ops");
                opexpr.opexprs = exprs;
                opexpr.ops = ops;
                return opexpr;
            }
            else
            {
                return Visit(context.operatorexpr0()[0]);
            }
        }

        public override LangObject VisitOperatorexpr0(TSParser.Operatorexpr0Context context)
        {
            var childRenCount = context.ChildCount;
            OperatorExpr expr = new OperatorExpr();
            switch (childRenCount)
            {
                case 1:
                    expr.constantOrFunctionApp= Visit(context.calcunit());
                    return expr;
                case 2:
                    expr.constantOrFunctionApp = Visit(context.calcunit());
                    expr.ops.Add(OperatorExpr.Operators.NOT);
                    return expr;
                case 3:
                    return Visit(context.operatorexpr());
                default:
                    break;
            }
            /*error*/
            Debug.Assert(false, "operator0 more than 3 child");
            return expr;
        }

        public override LangObject VisitCalcunit(TSParser.CalcunitContext context)
        {
            if (context.functionApp()!=null)
            {
                return Visit(context.functionApp());
            }
            return Visit(context.contantorvariable());
        }


        public override LangObject VisitVariable(TSParser.VariableContext context)
        {
            return new Variable(context.GetText());
        }

        public override LangObject VisitContantorvariable(TSParser.ContantorvariableContext context)
        {
            if(context.constant()!=null)
            {
                return Visit(context.constant());
            }
            return new Variable(context.GetText());
        }

        //public  LangObject VisitFunctionannotion(TSParser.FunctionannotionContext context)
        //{
        //    /*to do*/
        //    throw new NotImplementedException();
        //}

        //public  LangObject VisitTypesubstile(TSParser.TypesubstileContext context)
        //{
        //    throw new NotImplementedException();
        //}

        public override LangObject VisitType(TSParser.TypeContext context)
        {
            return new TSTypeAST(context.GetText()).getThis();
        }

        public override LangObject VisitFunctionname(TSParser.FunctionnameContext context)
        {
            FunctionName f = new FunctionName();
            
            foreach (var item in context.children)
            {
                if(item is TSParser.ModulenameContext)
                {
                    f.moduleName.Add(item.GetText());
                }
                else if(item.GetText()!="")
                {
                    f.functionName = item.GetText();
                }
            }
            return f;
        }

        public override LangObject VisitPatternSep(TSParser.PatternSepContext context)
        {
            return new LangObject();
        }

        public override LangObject VisitNum(TSParser.NumContext context)
        {
            if(context.GetText().Contains("."))
            {
                return new Constant(double.Parse(context.GetText()));
            }
            return new Constant(int.Parse(context.GetText()));
        }

        public override LangObject VisitString(TSParser.StringContext context)
        {
            return new Constant(context.GetText());
        }

        public override LangObject VisitBoolSymbol(TSParser.BoolSymbolContext context)
        {
            if(context.GetText()=="True")
            {
                return new Constant(true);
            }
            return new Constant(false);
        }

    }
}
